from j264.tools.boxes.AttributedObject import AttributedObject
from j264.tools.boxes.Box import Box
import json


class Patcher(AttributedObject):

    ### INITIALIZER ###

    def __init__(self):
        AttributedObject.__init__(self)
        self._patcher_attributes = { }
        self._children = []

    ### SPECIAL METHODS ###

    def __contains__(self, node):
        for x in self._children:
            if x is node:
                return True
        return False

    def __delitem__(self, i):
        nodes = self[i]
        if not isinstance(nodes, list):
            nodes = [nodes]
        for node in nodes:
            node._switch_parent(None)
        self._mark_entire_tree_for_later_update()

    def __getitem__(self, i):
        return self._children[i]

    def __iter__(self):
        for child in self._children:
            yield child

    def __len__(self):
        return len(self._children)

    def __setitem__(self, i, expr):
        if isinstance(i, int):
            assert isinstance(expr, Box)
            old = self[i]
            old._switch_parent(None)
            expr._switch_parent(self)
            self._children.insert(i, expr)

        else:
            assert all([isinstance(x, Box) for x in expr])
            if i.start == i.stop and i.start is not None \
                and i.stop is not None and i.start <= -len(self):
                start, stop = 0, 0
            else:
                start, stop, stride = i.indices(len(self))
            old = self[start:stop]
            for node in old:
                node._switch_parent(None)
            for node in expr:
                node._switch_parent(self)
            self._children.__setitem__(slice(start, start), expr)

        self._mark_entire_tree_for_later_update()

    ### READ-ONLY PUBLIC PROPERTIES ###

    @property
    def boxes(self):
        return self.children

    @property
    def children(self):
        return tuple(self._children)

    @property
    def lines(self):
        connections = []
        for child in self:
            connections.extend(child.connections)
        return tuple(set(connections))

    @property
    def max_obj_type(self):
        return 'patcher'

    ### PRIVATE METHODS ###

    def _get_json_data(self):
        attrs = self.max_attributes
        attrs['boxes'] = [x.json_data for x in self.boxes]
        attrs['lines'] = [x.json_data for x in self.lines]
        return {self.max_obj_type: attrs}

    ### PUBLIC METHODS ###

    def append(self, node):
        self.__setitem__(
            slice(len(self), len(self)),
            [node]
            )

    def extend(self, nodes):
        self.__setitem__(
            slice(len(self), len(self)),
            nodes.__getitem__(slice(0, len(nodes)))
            )

    def index(self, node):
        for i, element in enumerate(self._children):
            if element is node:
                return i
        else:
            raise ValueError('node {!r} not in {!r}.'.format(node, self))

    def insert(self, i, node):
        self.__setitem__(
            slice(i, i),
            [node]
            )

    def pop(self, i=-1):
        node = self[i]
        del(self[i])
        return node

    def remove(self, node):
        i = self.index(node)
        del(self[i])
